package org.xwalk.core;

import java.io.Serializable;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;

/**
 * This class represents a navigation history for a XWalkView instance.
 * It's not thread-safe and should be only called on UI thread.
 */

public class XWalkNavigationHistory   {

    private ArrayList<Object> constructorTypes;
    private ArrayList<Object> constructorParams;
    private ReflectMethod postWrapperMethod;

    /**
     * The direction for web page navigation.
     * @since 1.0
     */
    public enum Direction {
        /** The backward direction for web page navigation. */
        BACKWARD,
        /** The forward direction for web page navigation. */
        FORWARD
    }

    private ReflectMethod enumDirectionClassValueOfMethod = new ReflectMethod();

    private Object ConvertDirection(Direction type) {
        return enumDirectionClassValueOfMethod.invoke(type.toString());
    }

    private XWalkCoreWrapper coreWrapper;
    private Object bridge;

    protected Object getBridge() {
        return bridge;
    }

    public XWalkNavigationHistory(Object bridge) {
        this.bridge = bridge;
        reflectionInit();
    }

    /**
     * Total size of navigation history for the XWalkView.
     * @return the size of total navigation items.
     * @since 1.0
     */
    public int size() {
        try {
            return (Integer)sizeMethod.invoke();
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                throw new RuntimeException("Crosswalk's APIs are not ready yet");
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        return 0;
    }

    private ReflectMethod sizeMethod = new ReflectMethod(null, "size");

    /**
     * Test whether there is an item at a specific index.
     * @param index the given index.
     * @return true if there is an item at the specific index.
     * @since 1.0
     */
    public boolean hasItemAt(int index) {
        try {
            return (Boolean)hasItemAtintMethod.invoke(index);
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                throw new RuntimeException("Crosswalk's APIs are not ready yet");
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        return false;
    }

    private ReflectMethod hasItemAtintMethod = new ReflectMethod(null, "hasItemAt");

    /**
     * Get a specific item given by index.
     * @param index the given index.
     * @return the navigation item for the given index.
     * @since 1.0
     */
    public XWalkNavigationItem getItemAt(int index) {
        try {
            return (XWalkNavigationItem) coreWrapper.getWrapperObject(getItemAtintMethod.invoke(index));
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                throw new RuntimeException("Crosswalk's APIs are not ready yet");
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        return null;
    }

    private ReflectMethod getItemAtintMethod = new ReflectMethod(null, "getItemAt");

    /**
     * Get the current item which XWalkView displays.
     * @return the current navigation item.
     * @since 1.0
     */
    public XWalkNavigationItem getCurrentItem() {
        try {
            return (XWalkNavigationItem) coreWrapper.getWrapperObject(getCurrentItemMethod.invoke());
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                throw new RuntimeException("Crosswalk's APIs are not ready yet");
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        return null;
    }

    private ReflectMethod getCurrentItemMethod = new ReflectMethod(null, "getCurrentItem");

    /**
     * Test whether XWalkView can go back.
     * @return true if it can go back.
     * @since 1.0
     */
    public boolean canGoBack() {
        try {
            return (Boolean)canGoBackMethod.invoke();
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                throw new RuntimeException("Crosswalk's APIs are not ready yet");
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        return false;
    }

    private ReflectMethod canGoBackMethod = new ReflectMethod(null, "canGoBack");

    /**
     * Test whether XWalkView can go forward.
     * @return true if it can go forward.
     * @since 1.0
     */
    public boolean canGoForward() {
        try {
            return (Boolean)canGoForwardMethod.invoke();
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                throw new RuntimeException("Crosswalk's APIs are not ready yet");
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        return false;
    }

    private ReflectMethod canGoForwardMethod = new ReflectMethod(null, "canGoForward");

    /**
     * Navigates to the specified step from the current navigation item.
     * Do nothing if the offset is out of bound.
     * @param direction the direction of navigation.
     * @param steps go back or foward with a given steps.
     * @since 1.0
     */
    public void navigate(Direction direction, int steps) {
        try {
            navigateDirectionInternalintMethod.invoke(ConvertDirection(direction), steps);
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                throw new RuntimeException("Crosswalk's APIs are not ready yet");
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        
    }

    private ReflectMethod navigateDirectionInternalintMethod = new ReflectMethod(null, "navigate");

    /**
     * Get the index for current navigation item.
     * @return current index in the navigation history.
     * @since 1.0
     */
    public int getCurrentIndex() {
        try {
            return (Integer)getCurrentIndexMethod.invoke();
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                throw new RuntimeException("Crosswalk's APIs are not ready yet");
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        return 0;
    }

    private ReflectMethod getCurrentIndexMethod = new ReflectMethod(null, "getCurrentIndex");

    /**
     * Clear all history owned by this XWalkView.
     * @since 1.0
     */
    public void clear() {
        try {
            clearMethod.invoke();
        } catch (UnsupportedOperationException e) {
            if (coreWrapper == null) {
                throw new RuntimeException("Crosswalk's APIs are not ready yet");
            } else {
                XWalkCoreWrapper.handleRuntimeError(e);
            }
        }
        
    }

    private ReflectMethod clearMethod = new ReflectMethod(null, "clear");


    void reflectionInit() {
        XWalkCoreWrapper.initEmbeddedMode();

        coreWrapper = XWalkCoreWrapper.getInstance();
        if (coreWrapper == null) {
            XWalkCoreWrapper.reserveReflectObject(this);
            return;
        }

        enumDirectionClassValueOfMethod.init(null,
                coreWrapper.getBridgeClass("XWalkNavigationHistoryInternal$DirectionInternal"), "valueOf", String.class);

        sizeMethod.init(bridge, null,
                "sizeSuper");
        hasItemAtintMethod.init(bridge, null,
                "hasItemAtSuper", int.class);
        getItemAtintMethod.init(bridge, null,
                "getItemAtSuper", int.class);
        getCurrentItemMethod.init(bridge, null,
                "getCurrentItemSuper");
        canGoBackMethod.init(bridge, null,
                "canGoBackSuper");
        canGoForwardMethod.init(bridge, null,
                "canGoForwardSuper");
        navigateDirectionInternalintMethod.init(bridge, null,
                "navigateSuper", coreWrapper.getBridgeClass("XWalkNavigationHistoryInternal$DirectionInternal"), int.class);
        getCurrentIndexMethod.init(bridge, null,
                "getCurrentIndexSuper");
        clearMethod.init(bridge, null,
                "clearSuper");
    }

}
